#include "scanner.h"
#include <iostream>

Scanner::Scanner()
{}

ushort Scanner::startScanner(std::string s_code)
{
    char ch;
    for (uint i = 0; i < s_code.size();) {
        if (i == -1) return 1; // лексична помилка
        ch = s_code[i];
        if (isalpha(ch)) ch = 'A'; // ідентифікатор або ключове слово
        else if (isdigit(ch)) ch = 'D'; // числовий літерал
        switch(ch) {
        case '"' :
            i = __stringScanner(s_code, i);
            break;
        case '/' :
            if (s_code[i + 1] == '#') i = __commentScanner(s_code, i);
            else i = __commonScanner(s_code, i);
            break;
        case '#' :
            i = __commentScanner(s_code, i);
            break;
        case ' ' :
            i = __spaceScanner(s_code, i);
            break;
        case '\t' :
            i = __spaceScanner(s_code, i);
            break;
        case '\n' :
            i = __spaceScanner(s_code, i);
            break;
        case 'A' :
            i = __identificatorScanner(s_code, i);
            break;
        case 'D' :
            i = __digitsScanner(s_code, i);
            break;
            /* case '+' :
            if (isdigit(s_code[i + 1])) i = digitsScanner(s_code, i);
            else i = commonScanner(s_code, i);
            break; */
        default : i = __commonScanner(s_code, i);
        }
    }
    return 0;
}

ushort Scanner::__commentScanner(std::string s_code, uint pos)
{
    char ch;
    switch(s_code[pos]) {
    case '#' : // однорядковий коментар
        for (uint i = pos; i < s_code.size(); i++)
        {
            ch = s_code[i];
            if (ch == '\n') return i + 1;
        }
        break;
    case '/' : // багаторядковий коментар
        for (int i = pos + 2; i < s_code.size(); i++)
        {
            ch = s_code[i];
            if ((ch == '#') && (s_code[i + 1] == '/')) return i + 2;
        }
        return -1; // помилка. Відсутній закриваючий коментар - #/
    }
    return s_code.size();
}

ushort Scanner::__stringScanner(std::string s_code, uint pos)
{
    for (uint i = pos + 1; i < s_code.size(); i++) {
        if (s_code[i] == '"')
        {
            token_table.push_back(TokenEntry("literal", s_code.substr(pos + 1, i - pos - 1), Literal));
            return i + 1;
        }
    }
    return -1; // Помилка. Строковий літерал не має закриваючих лапок
}

ushort Scanner::__commonScanner(std::string s_code, uint pos)
{
    // var]|var)|var+|var*|var/|12+|12-|12)|12))|12))+
    if ((pos >= 1) && (isalpha(s_code[pos - 1]) || isdigit(s_code[pos - 1]) || isspace(s_code[pos - 1]) || (s_code[pos - 1] == ';') ||
                       (s_code[pos - 1] == ')') || (s_code[pos - 1] == ']') || (s_code[pos - 1] == '"') || (s_code[pos - 1] == '}')))
    {
        switch(s_code[pos]) {
        case '&' :
            if (s_code[pos + 1] == '&')
            {
                std::ostringstream s;
                s << s_code[pos] << s_code[pos + 1];
                token_table.push_back(TokenEntry(s.str(), Operation)); // операція &&
                return pos + 2;
            }
            token_table.push_back(TokenEntry(s_code[pos], Operation)); // логічна операція &
            return pos + 1;
            break;
        case '|' :
            if (s_code[pos + 1] == '|')
            {
                std::ostringstream s;
                s << s_code[pos] << s_code[pos + 1];
                token_table.push_back(TokenEntry(s.str(), Operation)); // операція ||
                return pos + 2;
            }
            token_table.push_back(TokenEntry(s_code[pos], Operation)); // логічна операція |
            return pos + 1;
        case ')' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        case '}' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        case ']' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        case ';' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        case ',' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        }
        if (keywords[s_code.substr(pos, 1)].token_type & Operation) // +,-,/,*,%,=
        {
            std::string operation = s_code.substr(pos, 1);
            if ((operation == "=") && (s_code[pos + 1] == '=')) // операція відношення "=="
            {
                operation += "=";
                token_table.push_back(TokenEntry(operation, keywords[operation].token_type));
                return pos + 2;
            }
            if ((operation == ">") && (s_code[pos + 1] == '=')) // операція відношення ">="
            {
                operation += "=";
                token_table.push_back(TokenEntry(operation, keywords[operation].token_type));
                return pos + 2;
            }
            if ((operation == "<") && (s_code[pos + 1] == '=')) // операція відношення "<="
            {
                operation += "=";
                token_table.push_back(TokenEntry(operation, keywords[operation].token_type));
                return pos + 2;
            }
            if ((operation == "!") && (s_code[pos + 1] == '=')) // операція відношення "!="
            {
                operation += "=";
                token_table.push_back(TokenEntry(operation, keywords[operation].token_type));
                return pos + 2;
            }
            token_table.push_back(TokenEntry(s_code[pos], keywords[operation].token_type));
            return pos + 1;
        }
    }
    if ((pos >= 1) && (isalpha(s_code[pos - 1]) || isspace(s_code[pos - 1]))) // var=digit|var  =digit|var[
    {
        switch(s_code[pos]) {
        case '=' :
            token_table.push_back(TokenEntry(s_code[pos], Appropriation));
            return pos + 1;
        case '[' :
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        }
    }
    if ((pos >= 1) && !isdigit(s_code[pos - 1])) // var()|var+(digit)|+((
    {
        if (s_code[pos] == '(')
        {
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            if (s_code[pos + 1] == ')')
            {
                token_table.push_back(TokenEntry(s_code[pos + 1], Separator));
                return pos + 2;
            }
            return pos + 1;
        }
        else if (s_code[pos] == '{')
        {
            token_table.push_back(TokenEntry(s_code[pos], Separator));
            return pos + 1;
        }
    }
    if ((pos >= 1) && (!isdigit(pos - 1) || !isalpha(pos - 1)))
        if (s_code[pos] == '!')
        {
            token_table.push_back(TokenEntry(s_code[pos], Operation));
            return pos + 1;
        }
    return -1; // лексична помилка
}

ushort Scanner::__digitsScanner(std::string s_code, uint pos)
{
    enum State {H, R, G, E};
    std::string str;
    int i;
    State st = H;
    for(i = pos; i < s_code.size();) {
        if (st == E) break;
        switch (st) {
        case H :
            if ((s_code[i] == '+') || (s_code[i] == '-') || isdigit(s_code[i])) { str += s_code[i++]; st = R;}
            else st = E;
            break;
        case R : // ціла частина
            if (isdigit(s_code[i])) str += s_code[i++];
            else if (s_code[i] == '.') { str += '.'; st = G; ++i;}
            else if (isalpha(s_code[i])) return - 1; // 12var - помилка
            else st = E;
            break;
        case G : // дробова частина
            if (isdigit(s_code[i])) str += s_code[i++];
            else st = E;
        }
    }
    token_table.push_back(TokenEntry("literal", str, Literal));
    return i;
}

ushort Scanner::__identificatorScanner(std::string s_code, uint pos)
{
    std::string str;
    uint i;
    for (i = pos; i < s_code.size(); i++) {
        if (isalpha(s_code[i]) || isdigit(s_code[i]))
            str += s_code[i];
        else break;
    }
    TokenType t;
    if (keywords[str].token_type == Keyword) t = Keyword;
    else if (keywords[str].token_type & Operation) t = keywords[str].token_type;
    else if (s_code[i] == '(') // описання ф-ції
    {
        token_table.push_back(TokenEntry("funcname", str, Function)); // ф-ції в грамматиці позначаються VT-символом funcname
        return i;
    }
    else // змінна
    {
        t = Identificator;
        token_table.push_back(TokenEntry("varname", str, t));
        return i;
    }
    token_table.push_back(TokenEntry(str, t));
    return i;
}

ushort Scanner::__spaceScanner(std::string s_code, uint pos)
{
    for (uint i = pos; i < s_code.size(); i++) {
        if (!isspace(s_code[i])) return i;
    }
    return s_code.size();
}
